/**
 * Kratos Multi-Physics
 *
 * Main authors:    Riccardo Rossi
 *                  Vicente Mataix Ferrandiz
 *
 * Copyright (c) 2016, Pooyan Dadvand, Riccardo Rossi, CIMNE (International Center for Numerical Methods in Engineering)
 * All rights reserved.
 *
 *  */

/**
 * @brief The dictionary to organize the parameter data as a tree of indexed nodes.
 *
 * the class from Kratos. and add `GetValueByPath` and `LookupOrDefault` methods.
 *
 * @author nene & wxc
 * @date November, 2024.
 *
 */
#include "Utility/Configuration/SGParameter.h"

#include <algorithm>
#include <fstream>
#include <stack>

#include <boost/algorithm/string.hpp>

#include "Utility/Exception/SGException.h"
#include "Utility/Logging/SGLogger.h"

#define EXCEPTION throw SG::Utility::SGException ("Error")
#define EXCEPTION_IF(conditional) \
    if (conditional)              \
    EXCEPTION
#define EXCEPTION_IF_NOT(conditional) \
    if (!(conditional))               \
    EXCEPTION

namespace SG::Utility
{
    SGParameter::iterator_adaptor::iterator_adaptor (SGParameter::iterator_adaptor::value_iterator itValue,
                                                     nlohmann::json*                               pValue,
                                                     std::shared_ptr<nlohmann::json>               pRoot)
        : mDistance (std::distance (pValue->begin (), itValue)), mrValue (*pValue), mpParameters (new SGParameter (itValue, pValue, pRoot))
    {}

    /***********************************************************************************/

    SGParameter::iterator_adaptor::iterator_adaptor (const SGParameter::iterator_adaptor& itValue)
        : mDistance (itValue.mDistance), mrValue (itValue.mrValue),
          mpParameters (new SGParameter (itValue->GetUnderlyingStorage (), itValue->GetUnderlyingRootStorage ()))
    {}

    /***********************************************************************************/

    SGParameter::iterator_adaptor& SGParameter::iterator_adaptor::operator++ ()
    {
        ++mDistance;
        return *this;
    }

    /***********************************************************************************/

    SGParameter::iterator_adaptor SGParameter::iterator_adaptor::operator++ (int)
    {
        iterator_adaptor tmp (*this);
        operator++ ();
        return tmp;
    }

    /***********************************************************************************/

    bool SGParameter::iterator_adaptor::operator== (const SGParameter::iterator_adaptor& rhs) const
    {
        return mDistance == rhs.mDistance;
    }

    /***********************************************************************************/

    bool SGParameter::iterator_adaptor::operator!= (const SGParameter::iterator_adaptor& rhs) const
    {
        return mDistance != rhs.mDistance;
    }

    /***********************************************************************************/

    SGParameter& SGParameter::iterator_adaptor::operator* () const
    {
        auto it = GetCurrentIterator ();
        if (it != mrValue.end ())
        {
            mpParameters->mpValue = &(*it);
        }
        return *mpParameters;
    }

    /***********************************************************************************/

    SGParameter* SGParameter::iterator_adaptor::operator->() const
    {
        auto it = GetCurrentIterator ();
        if (it != mrValue.end ())
        {
            mpParameters->mpValue = &(*it);
        }
        return mpParameters.get ();
    }

    /***********************************************************************************/

    inline SGParameter::iterator_adaptor::value_iterator SGParameter::iterator_adaptor::GetCurrentIterator () const
    {
        auto it = mrValue.begin ();
        for (std::size_t i = 0; i < mDistance; ++i)
            it++;
        return it;
    }

    /***********************************************************************************/

    const std::string SGParameter::iterator_adaptor::name ()
    {
        auto it = GetCurrentIterator ();
        return it.key ();
    }

    /***********************************************************************************/

    SGParameter::const_iterator_adaptor::const_iterator_adaptor (value_iterator                  itValue,
                                                                 nlohmann::json*                 pValue,
                                                                 std::shared_ptr<nlohmann::json> pRoot)
        : mDistance (std::distance (pValue->cbegin (), itValue)), mrValue (*pValue), mpParameters (new SGParameter (itValue, pValue, pRoot))
    {}

    /***********************************************************************************/

    SGParameter::const_iterator_adaptor::const_iterator_adaptor (const const_iterator_adaptor& itValue)
        : mDistance (itValue.mDistance), mrValue (itValue.mrValue),
          mpParameters (new SGParameter (itValue->GetUnderlyingStorage (), itValue->GetUnderlyingRootStorage ()))
    {}

    /***********************************************************************************/

    SGParameter::const_iterator_adaptor& SGParameter::const_iterator_adaptor::operator++ ()
    {
        ++mDistance;
        return *this;
    }

    /***********************************************************************************/

    SGParameter::const_iterator_adaptor SGParameter::const_iterator_adaptor::operator++ (int)
    {
        const_iterator_adaptor tmp (*this);
        operator++ ();
        return tmp;
    }

    /***********************************************************************************/

    bool SGParameter::const_iterator_adaptor::operator== (const SGParameter::const_iterator_adaptor& rhs) const
    {
        return mDistance == rhs.mDistance;
    }

    /***********************************************************************************/

    bool SGParameter::const_iterator_adaptor::operator!= (const SGParameter::const_iterator_adaptor& rhs) const
    {
        return mDistance != rhs.mDistance;
    }

    /***********************************************************************************/

    const SGParameter& SGParameter::const_iterator_adaptor::operator* () const
    {
        auto it = GetCurrentIterator ();
        if (it != mrValue.cend ())
            mpParameters->mpValue = const_cast<nlohmann::json*> (&(*it));
        return *mpParameters;
    }

    /***********************************************************************************/

    const SGParameter* SGParameter::const_iterator_adaptor::operator->() const
    {
        auto it = GetCurrentIterator ();
        if (it != mrValue.cend ())
            mpParameters->mpValue = const_cast<nlohmann::json*> (&(*it));
        return mpParameters.get ();
    }

    /***********************************************************************************/

    inline SGParameter::const_iterator_adaptor::value_iterator SGParameter::const_iterator_adaptor::GetCurrentIterator () const
    {
        auto it = mrValue.cbegin ();
        for (std::size_t i = 0; i < mDistance; ++i)
            it++;
        return it;
    }

    /***********************************************************************************/

    const std::string SGParameter::const_iterator_adaptor::name ()
    {
        auto it = GetCurrentIterator ();
        return it.key ();
    }

    /***********************************************************************************/

    SGParameter::SGParameter ()
    {
        mpRoot  = std::make_shared<nlohmann::json> (nlohmann::json::parse ("{}", nullptr, true, true));
        mpValue = mpRoot.get ();
    }

    /***********************************************************************************/

    nlohmann::json SGParameter::ReadFile (const boost::filesystem::path& rFileName)
    {
        std::ifstream new_file;
        new_file.open (rFileName.string (), std::ios::in);

        std::stringstream strStream;
        strStream << new_file.rdbuf ();
        std::string input_json = strStream.str ();
        return nlohmann::json::parse (input_json);
    }

    static nlohmann::json keys_to_upper (const nlohmann::json& j)
    {
        if (j.is_object ())
        {
            nlohmann::json new_obj = nlohmann::json::object ();
            for (auto it = j.begin (); it != j.end (); ++it)
            {
                new_obj[boost::algorithm::to_upper_copy (it.key ())] = keys_to_upper (it.value ());
            }
            return new_obj;
        }
        else if (j.is_array ())
        {
            nlohmann::json new_arr = nlohmann::json::array ();
            for (const auto& item : j)
            {
                new_arr.push_back (keys_to_upper (item));
            }
            return new_arr;
        }
        else
        {
            return j;
        }
    }

    /***********************************************************************************/

    SGParameter::SGParameter (const std::string& rJsonString)
    {
        mpRoot  = std::make_shared<nlohmann::json> (nlohmann::json::parse (rJsonString, nullptr, true, true));
        mpRoot  = std::make_shared<nlohmann::json> (keys_to_upper (*mpRoot.get ()));
        mpValue = mpRoot.get ();

        // A stack containing the current sequence of included JSONs
        std::vector<boost::filesystem::path> include_sequence;

        // Recursively resolve json links
        SolveIncludes (*mpValue, "root", include_sequence);
    }

    /***********************************************************************************/

    void SGParameter::SolveIncludes (nlohmann::json&                       rJson,
                                     const boost::filesystem::path&        rFileName,
                                     std::vector<boost::filesystem::path>& rIncludeSequence)
    {
        std::stack<std::pair<nlohmann::json*, nlohmann::json::iterator>> s;

        if (rJson.is_object ())
        {
            s.push ({ &rJson, rJson.begin () });
        }

        while (!s.empty ())
        {
            std::pair<nlohmann::json*, nlohmann::json::iterator> pJson_it = s.top ();
            s.pop ();

            nlohmann::json*          act_pJson = pJson_it.first;
            nlohmann::json::iterator act_it    = pJson_it.second;

            while (act_it != act_pJson->end ())
            {
                if (act_it.value ().is_object ())
                {
                    s.emplace (&act_it.value (), act_it.value ().begin ());
                }
                else if (act_it.value ().is_array ())
                {
                    for (auto it : act_it.value ().items ())
                    {
                        SolveIncludes (it.value (), rFileName, rIncludeSequence);
                    }
                }
                // else if (act_it.key () == "@include_json")
                // {
                //     // Check whether the included file exists
                //     const std::string           included_file_path_string = *act_it;
                //     const std::filesystem::path included_file_path =
                //         FilesystemExtensions::ResolveSymlinks (std::filesystem::path (included_file_path_string));
                //     EXCEPTION_IF_NOT (std::filesystem::is_regular_file (included_file_path)) << "File not found: '" << *act_it << "'";

                //     nlohmann::json included_json = ReadFile (included_file_path);

                //     // Check for cycles and push to the include sequence
                //     auto it_cycle = std::find (rIncludeSequence.begin (), rIncludeSequence.end (), included_file_path);
                //     if (it_cycle != rIncludeSequence.end ())
                //     {
                //         std::stringstream message_stream;
                //         message_stream << "Include cycle in json files: ";
                //         for (; it_cycle != rIncludeSequence.end (); ++it_cycle)
                //         {
                //             message_stream << *it_cycle << " => ";
                //         }
                //         message_stream << included_file_path << " => ...";
                //         SGError << message_stream.str ();
                //     }
                //     rIncludeSequence.push_back (included_file_path);

                //     // Resolve links in the included json
                //     SolveIncludes (included_json, included_file_path, rIncludeSequence);
                //     rIncludeSequence.pop_back ();

                //     // Remove the @include entry
                //     act_it = act_pJson->erase (act_it);

                //     // Add the new entries due to the new included file
                //     act_pJson->insert (included_json.begin (), included_json.end ());
                //     continue;
                // }
                ++act_it;
            }
        }
    }

    /***********************************************************************************/

    SGParameter::SGParameter (std::ifstream& rStringStream)
    {
        mpRoot  = std::make_shared<nlohmann::json> (nlohmann::json::parse (rStringStream, nullptr, true, true));
        mpValue = mpRoot.get ();

        // A stack containing the current sequence of included JSONs
        std::vector<boost::filesystem::path> include_sequence;

        // Recursively resolve json links
        SolveIncludes (*mpValue, "root", include_sequence);
    }

    /***********************************************************************************/

    SGParameter::SGParameter (SGParameter const& rOther)
    {
        mpRoot  = rOther.mpRoot;
        mpValue = rOther.mpValue;
    }

    /***********************************************************************************/

    SGParameter::SGParameter (SGParameter&& rOther) : SGParameter ()
    {
        swap (rOther);
    }

    /***********************************************************************************/

    SGParameter& SGParameter::operator= (SGParameter const& rOther)
    {
        if (mpRoot.get () == mpValue || mpRoot == nullptr)
        {
            mpRoot  = std::make_shared<nlohmann::json> (nlohmann::json::parse (rOther.WriteJsonString (), nullptr, true, true));
            mpValue = mpRoot.get ();
        }
        else
        {
            *mpValue = nlohmann::json (nlohmann::json::parse (rOther.WriteJsonString (), nullptr, true, true));
            // note that mpRoot is unchanged
        }

        return *this;
    }

    /***********************************************************************************/

    SGParameter SGParameter::operator[] (const std::string& rEntry)
    {
        return this->GetValue (boost::to_upper_copy (rEntry));
    }

    /***********************************************************************************/

    SGParameter SGParameter::operator[] (const std::string& rEntry) const
    {
        return this->GetValue (boost::to_upper_copy (rEntry));
    }

    /***********************************************************************************/

    SGParameter SGParameter::operator[] (const IndexType Index)
    {
        return this->GetArrayItem (Index);
    }

    /***********************************************************************************/

    SGParameter SGParameter::operator[] (const IndexType Index) const
    {
        return this->GetArrayItem (Index);
    }

    /***********************************************************************************/

    SGParameter& SGParameter::operator= (SGParameter&& rOther)
    {
        swap (rOther);
        return *this;
    }

    /***********************************************************************************/

    SGParameter SGParameter::Clone () const
    {
        return SGParameter (WriteJsonString ());  // new json(*mpValue));
    }

    /***********************************************************************************/

    const std::string SGParameter::WriteJsonString () const
    {
        return mpValue->dump ();
    }

    /***********************************************************************************/

    const std::string SGParameter::PrettyPrintJsonString () const
    {
        return mpValue->dump (4);
    }

    SGParameter SGParameter::GetValueByPath (std::string path)
    {
        nlohmann::json* current = mpValue;

        std::vector<std::string> parts;
        boost::split (parts, path, boost::is_any_of ("/"));

        for (const auto& part : parts)
        {
            if (part.empty ())
                continue;  // 跳过空部分

            if (part.find ('[') != std::string::npos)
            {
                // 处理数组索引
                size_t      bracket_pos = part.find ('[');
                std::string array_key   = boost::to_upper_copy (part.substr (0, bracket_pos));
                size_t      index       = std::stoi (part.substr (bracket_pos + 1, part.find (']') - bracket_pos - 1));
                if (current->contains (array_key) && current->at (array_key).is_array () && index < current->at (array_key).size ())
                {
                    current = &(current->at (array_key).at (index));
                }
                else
                {
                    return {};
                }
            }
            else
            {
                auto upart = boost::to_upper_copy (part);
                // 处理普通键
                if (current->contains (upart))
                {
                    current = &(current->at (upart));
                }
                else
                {
                    return {};
                }
            }
        }

        return SGParameter (current, mpRoot);
    }

    /***********************************************************************************/

    SGParameter SGParameter::GetValue (const std::string& rEntry)
    {
        // SGInfo << mpValue->dump ();
        auto j = mpValue->find (boost::to_upper_copy (rEntry));
        EXCEPTION_IF (j == mpValue->end ()) << "Getting a value that does not exist. entry string : " << rEntry << std::endl;
        return SGParameter (&(*j), mpRoot);
    }

    /***********************************************************************************/

    SGParameter SGParameter::GetValue (const std::string& rEntry) const
    {
        auto j = mpValue->find (boost::algorithm::to_upper_copy (rEntry));
        EXCEPTION_IF (j == mpValue->end ()) << "Getting a value that does not exist. entry string : " << rEntry << std::endl;
        return SGParameter (&(*j), mpRoot);
    }

    /***********************************************************************************/

    void SGParameter::SetValue (const std::string& rEntry, const SGParameter& rOtherValue)
    {
        auto upEntry = boost::to_upper_copy (rEntry);
        EXCEPTION_IF (mpValue->find (upEntry) == mpValue->end ()) << "Value must exist to be set. Use AddValue instead" << std::endl;
        (*mpValue)[upEntry] = *(rOtherValue.mpValue);
    }

    /***********************************************************************************/

    void SGParameter::AddValue (const std::string& rEntry, const SGParameter& rOtherValue)
    {
        auto upEntry = boost::to_upper_copy (rEntry);
        if (mpValue->find (upEntry) == mpValue->end ())
        {
            (*mpValue)[upEntry] = *(rOtherValue.mpValue);
        }
        else
        {
            SGWarn << "WARNING:: Entry " << upEntry << " already defined. Overwriting it" << std::endl;
            SetValue (upEntry, rOtherValue);
        }
    }

    /***********************************************************************************/

    SGParameter SGParameter::AddEmptyValue (const std::string& rEntry)
    {
        auto upEntry = boost::to_upper_copy (rEntry);
        if (this->Has (upEntry) == false)
        {
            return SGParameter (&(*mpValue)[upEntry], mpRoot);
        }
        return this->GetValue (upEntry);
    }

    /***********************************************************************************/

    bool SGParameter::RemoveValue (const std::string& rEntry)
    {
        auto upEntry = boost::to_upper_copy (rEntry);
        return static_cast<bool> (mpValue->erase (upEntry));
    }

    /***********************************************************************************/

    bool SGParameter::RemoveValues (const std::vector<std::string>& rEntries)
    {
        for (const auto& r_entry : rEntries)
        {
            if (!this->Has (r_entry))
                return false;
        }
        for (const auto& r_entry : rEntries)
        {
            this->RemoveValue (r_entry);
        }
        return true;
    }

    /***********************************************************************************/

    SGParameter::json_iteration_proxy SGParameter::items () noexcept
    {
        return mpValue->items ();
    }

    /***********************************************************************************/

    SGParameter::json_const_iteration_proxy SGParameter::items () const noexcept
    {
        return json_const_iteration_proxy (*mpValue);
    }

    /***********************************************************************************/

    bool SGParameter::Has (const std::string& rEntry) const
    {
        return mpValue->contains (boost::algorithm::to_upper_copy (rEntry));
    }

    /***********************************************************************************/

    bool SGParameter::IsNull () const
    {
        return mpValue->is_null ();
    }

    /***********************************************************************************/

    bool SGParameter::IsNumber () const
    {
        return mpValue->is_number ();
    }

    /***********************************************************************************/

    bool SGParameter::IsDouble () const
    {
        return mpValue->is_number_float ();
    }

    /***********************************************************************************/

    bool SGParameter::IsInt () const
    {
        return mpValue->is_number_integer ();
    }

    /***********************************************************************************/

    bool SGParameter::IsBool () const
    {
        return mpValue->is_boolean ();
    }

    /***********************************************************************************/

    bool SGParameter::IsString () const
    {
        return mpValue->is_string ();
    }

    /***********************************************************************************/

    bool SGParameter::IsArray () const
    {
        return mpValue->is_array ();
    }

    /***********************************************************************************/

    bool SGParameter::IsStringArray () const
    {
        if (!mpValue->is_array ())
        {
            return false;
        }
        else
        {
            const auto& r_array = (*mpValue);
            for (IndexType i = 0; i < mpValue->size (); ++i)
            {
                if (!r_array[i].is_string ())
                {
                    return false;
                }
            }
            return true;  // All entries are strings or Vector is empty
        }
    }

    /***********************************************************************************/

    // bool SGParameter::IsVector () const
    // {
    //     if (!mpValue->is_array ())
    //         return false;

    //     auto& r_array = (*mpValue);
    //     for (IndexType i = 0; i < mpValue->size (); ++i)
    //     {
    //         if (!r_array[i].is_number ())
    //             return false;
    //     }
    //     return true;  // All entries are numbers or Vector is empty
    // }

    /***********************************************************************************/

    // bool SGParameter::IsMatrix () const
    // {
    //     if (!mpValue->is_array ())  // mpValue != [ ... ]
    //         return false;

    //     const SizeType nrows = mpValue->size ();
    //     if (nrows == 0)  // mpValue is an empty array/vector => "[]"
    //         return false;

    //     for (IndexType i = 0; i < nrows; ++i)
    //     {
    //         auto& row_i = (*mpValue)[i];
    //         if (!row_i.is_array ())
    //             return false;

    //         IndexType ncols = row_i.size ();
    //         if (ncols != (*mpValue)[0].size ())  // Compare number of columns to first row
    //             return false;                    // Number of columns is not consistent

    //         for (IndexType j = 0; j < ncols; ++j)
    //         {                                    // Check all values in column
    //             if (!row_i[j].is_number ())
    //                 return false;
    //         }
    //     }

    //     return true;  // All entries are numbers or Matrix is empty ([[]] or
    //                   // [[],[],[],...])
    // }

    /***********************************************************************************/

    bool SGParameter::IsSubParameter () const
    {
        return mpValue->is_object ();
    }

    /***********************************************************************************/

    double SGParameter::GetDouble () const
    {
        EXCEPTION_IF_NOT (mpValue->is_number ()) << "Argument must be a number" << std::endl;
        return mpValue->get<double> ();
    }

    /***********************************************************************************/

    int SGParameter::GetInt () const
    {
        EXCEPTION_IF_NOT (mpValue->is_number ()) << "Argument must be a number" << std::endl;
        return mpValue->get<int> ();
    }

    /***********************************************************************************/

    bool SGParameter::GetBool () const
    {
        EXCEPTION_IF_NOT (mpValue->is_boolean ()) << "Argument must be a bool" << std::endl;
        return mpValue->get<bool> ();
    }

    /***********************************************************************************/

    std::string SGParameter::GetString () const
    {
        EXCEPTION_IF_NOT (mpValue->is_string ()) << "Argument must be a string" << std::endl;
        return mpValue->get<std::string> ();
    }

    /***********************************************************************************/

    std::vector<std::string> SGParameter::GetStringArray () const
    {
        EXCEPTION_IF_NOT (this->IsStringArray ()) << "Argument must be a string array" << std::endl;
        std::vector<std::string> result (this->size ());
        for (std::size_t i = 0; i < result.size (); ++i)
        {
            result[i] = this->GetArrayItem (i).GetString ();
        }
        return result;
    }

    /***********************************************************************************/

    // Vector SGParameter::GetVector () const
    // {
    //     EXCEPTION_IF_NOT (mpValue->is_array ()) << "Argument must be a Vector (a json list)" << std::endl;

    //     const SizeType size = mpValue->size ();

    //     Vector aux_V (size);

    //     for (IndexType i = 0; i < size; ++i)
    //     {
    //         EXCEPTION_IF_NOT ((*mpValue)[i].is_number ()) << "Entry " << i << " is not a number!" << std::endl;
    //         aux_V (i) = (*mpValue)[i].get<double> ();
    //     }

    //     return aux_V;
    // }

    // /***********************************************************************************/

    // Matrix SGParameter::GetMatrix () const
    // {
    //     EXCEPTION_IF_NOT (mpValue->is_array ()) << "Argument must be a Matrix (a json list of lists)" << std::endl;

    //     const SizeType nrows = mpValue->size ();
    //     EXCEPTION_IF (nrows == 0) << "Argument must be a Matrix (a json list of lists)" << std::endl;

    //     IndexType ncols = 0;
    //     if ((*mpValue)[0].is_array ())
    //         ncols = (*mpValue)[0].size ();

    //     Matrix aux_A (nrows, ncols);

    //     for (IndexType i = 0; i < nrows; ++i)
    //     {
    //         auto& row_i = (*mpValue)[i];
    //         EXCEPTION_IF_NOT (row_i.is_array ()) << "Not an array on row " << i << std::endl;
    //         EXCEPTION_IF_NOT (row_i.size () == ncols) << "Wrong size of row " << i << std::endl;
    //         for (IndexType j = 0; j < ncols; ++j)
    //         {
    //             EXCEPTION_IF_NOT ((row_i)[j].is_number ()) << "Entry (" << i << "," << j << ") is not a number!" << std::endl;
    //             aux_A (i, j) = (row_i)[j].get<double> ();
    //         }
    //     }

    //     return aux_A;
    // }

    /***********************************************************************************/

#define KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS(TYPE, TYPE_NAME)                                  \
    template <>                                                                                    \
    Utility_Configuration_DECLSPEC bool SGParameter::Is<TYPE> () const                             \
    {                                                                                              \
        return this->Is##TYPE_NAME ();                                                             \
    }                                                                                              \
    template <>                                                                                    \
    Utility_Configuration_DECLSPEC TYPE SGParameter::Get<TYPE> () const                            \
    {                                                                                              \
        return this->Get##TYPE_NAME ();                                                            \
    }                                                                                              \
    template <>                                                                                    \
    Utility_Configuration_DECLSPEC TYPE SGParameter::LookupOrDefault<TYPE> (TYPE defaultVal) const \
    {                                                                                              \
        if (Is<TYPE> ())                                                                           \
            return this->Get##TYPE_NAME ();                                                        \
        else                                                                                       \
            return defaultVal;                                                                     \
    }                                                                                              \
    template <>                                                                                    \
    Utility_Configuration_DECLSPEC void SGParameter::Set<TYPE> (const TYPE& rValue)                \
    {                                                                                              \
        this->Set##TYPE_NAME (rValue);                                                             \
    }

    KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (double, Double)

    KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (int, Int)

    KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (bool, Bool)

    KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (std::string, String)

    KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (std::vector<std::string>, StringArray)

    KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (std::complex<double>, Complex)

    // KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (Vector, Vector)

    // KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS (Matrix, Matrix)

#undef KRATOS_DEFINE_PARAMETERS_VALUE_ACCESSORS

    // Is, Get, and Set for subparameters

    template <>
    bool SGParameter::Is<SGParameter> () const
    {
        return this->IsSubParameter ();
    }

    /***********************************************************************************/

    void SGParameter::SetDouble (const double Value)
    {
        *mpValue = Value;
    }

    /***********************************************************************************/

    void SGParameter::SetInt (const int Value)
    {
        *mpValue = Value;
    }

    /***********************************************************************************/

    void SGParameter::SetBool (const bool Value)
    {
        *mpValue = Value;
    }

    /***********************************************************************************/

    void SGParameter::SetString (const std::string& rValue)
    {
        *mpValue = rValue;
    }

    /***********************************************************************************/

    void SGParameter::SetStringArray (const std::vector<std::string>& rValue)
    {
        const SizeType size = rValue.size ();

        nlohmann::json j_array (0.0, size);
        (*mpValue) = j_array;

        for (IndexType i = 0; i < size; ++i)
        {
            (*mpValue)[i] = rValue[i];
        }
    }

    /***********************************************************************************/

    // void SGParameter::SetVector (const Vector& rValue)
    // {
    //     const SizeType size = rValue.size ();

    //     nlohmann::json j_array (0.0, size);
    //     (*mpValue) = j_array;

    //     for (IndexType i = 0; i < size; ++i)
    //     {
    //         (*mpValue)[i] = rValue[i];
    //     }
    // }

    /***********************************************************************************/

    // void SGParameter::SetMatrix (const Matrix& rValue)
    // {
    //     const SizeType nrows = rValue.size1 ();
    //     const SizeType ncols = rValue.size2 ();

    //     nlohmann::json j_col_array (0.0, ncols);
    //     nlohmann::json j_row_array (0.0, nrows);
    //     (*mpValue) = j_row_array;

    //     for (IndexType i = 0; i < nrows; ++i)
    //     {
    //         (*mpValue)[i] = j_col_array;

    //         for (IndexType j = 0; j < ncols; ++j)
    //         {
    //             (*mpValue)[i][j] = rValue (i, j);
    //         }
    //     }
    // }

    /***********************************************************************************/

    void SGParameter::AddDouble (const std::string& rEntry, const double Value)
    {
        auto        upEntry = boost::to_upper_copy (rEntry);
        SGParameter aux_parameters (R"({"value": 0.0})");
        aux_parameters["value"].SetDouble (Value);
        this->AddValue (upEntry, aux_parameters["value"]);
    }

    /***********************************************************************************/

    void SGParameter::AddInt (const std::string& rEntry, const int Value)
    {
        auto        upEntry = boost::to_upper_copy (rEntry);
        SGParameter aux_parameters (R"({"value": 0})");
        aux_parameters["value"].SetInt (Value);
        this->AddValue (upEntry, aux_parameters["value"]);
    }

    /***********************************************************************************/

    void SGParameter::AddBool (const std::string& rEntry, const bool Value)
    {
        auto        upEntry = boost::to_upper_copy (rEntry);
        SGParameter aux_parameters (R"({"value": false})");
        aux_parameters["value"].SetBool (Value);
        this->AddValue (upEntry, aux_parameters["value"]);
    }

    /***********************************************************************************/

    void SGParameter::AddString (const std::string& rEntry, const std::string& rValue)
    {
        auto        upEntry = boost::to_upper_copy (rEntry);
        SGParameter aux_parameters (R"({"value": ""})");
        aux_parameters["value"].SetString (rValue);
        this->AddValue (upEntry, aux_parameters["value"]);
    }

    /***********************************************************************************/

    void SGParameter::AddStringArray (const std::string& rEntry, const std::vector<std::string>& rValue)
    {
        auto        upEntry = boost::to_upper_copy (rEntry);
        SGParameter aux_parameters (R"({"value": []})");
        aux_parameters["value"].SetStringArray (rValue);
        this->AddValue (upEntry, aux_parameters["value"]);
    }

    /***********************************************************************************/

    // void SGParameter::AddVector (const std::string& rEntry, const Vector& rValue)
    // {
    //     SGParameter aux_parameters (R"({"value": []})");
    //     aux_parameters["value"].SetVector (rValue);
    //     this->AddValue (rEntry, aux_parameters["value"]);
    // }

    /***********************************************************************************/

    // void SGParameter::AddMatrix (const std::string& rEntry, const Matrix& rValue)
    // {
    //     SGParameter aux_parameters (R"({"value": []})");
    //     aux_parameters["value"].SetMatrix (rValue);
    //     this->AddValue (rEntry, aux_parameters["value"]);
    // }

    /***********************************************************************************/

    SGParameter::iterator SGParameter::begin ()
    {
        return iterator (mpValue->begin (), mpValue, mpRoot);
    }

    /***********************************************************************************/

    SGParameter::iterator SGParameter::end ()
    {
        return iterator (mpValue->end (), mpValue, mpRoot);
    }

    /***********************************************************************************/

    SGParameter::const_iterator SGParameter::begin () const
    {
        return const_iterator (mpValue->cbegin (), mpValue, mpRoot);
    }

    /***********************************************************************************/

    SGParameter::const_iterator SGParameter::end () const
    {
        return const_iterator (mpValue->cend (), mpValue, mpRoot);
    }

    /***********************************************************************************/

    SGParameter::SizeType SGParameter::size () const
    {
        EXCEPTION_IF_NOT (mpValue->is_array ()) << "Size can only be queried if the value is of Array type" << std::endl;
        return mpValue->size ();
    }

    /***********************************************************************************/

    void SGParameter::swap (SGParameter& rOther) noexcept
    {
        std::swap (mpValue, rOther.mpValue);
        std::swap (mpRoot, rOther.mpRoot);
    }

    /***********************************************************************************/

    void SGParameter::Reset () noexcept
    {
        SGParameter p;
        swap (p);
    }

    /***********************************************************************************/

    SGParameter SGParameter::GetArrayItem (const IndexType Index)
    {
        if (mpValue->is_array () == false)
        {
            SGError << "GetArrayItem only makes sense if the value if of Array type" << std::endl;
            return SGParameter ();
        }
        else
        {
            EXCEPTION_IF (Index >= mpValue->size ()) << "Index exceeds array size. Index value is : " << Index << std::endl;
            return SGParameter (&((*mpValue)[Index]), mpRoot);
        }
    }

    /***********************************************************************************/

    SGParameter SGParameter::GetArrayItem (const IndexType Index) const
    {
        if (mpValue->is_array () == false)
        {
            SGError << "GetArrayItem only makes sense if the value if of Array type" << std::endl;
            return SGParameter ();
        }
        else
        {
            EXCEPTION_IF (Index >= mpValue->size ()) << "Index exceeds array size. Index value is : " << Index << std::endl;
            return SGParameter (&((*mpValue)[Index]), mpRoot);
        }
    }

    /***********************************************************************************/

    void SGParameter::SetArrayItem (const IndexType Index, const SGParameter& rOtherArrayItem)
    {
        if (mpValue->is_array () == false)
        {
            SGError << "SetArrayItem only makes sense if the value if of Array type" << std::endl;
        }
        else
        {
            EXCEPTION_IF (Index >= mpValue->size ()) << "Index exceeds array size. Index value is : " << Index << std::endl;
            (*mpValue)[Index] = *rOtherArrayItem.mpValue;
        }
    }

    /***********************************************************************************/

    void SGParameter::AddEmptyArray (const std::string& rEntry)
    {
        auto upEntry = boost::to_upper_copy (rEntry);
        if (mpValue->find (upEntry) == mpValue->end ())
        {
            nlohmann::json j_array (nlohmann::json::value_t::array);
            (*mpValue)[upEntry] = j_array;
        }
    }

    /***********************************************************************************/

    void SGParameter::Append (const double Value)
    {
        EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
        nlohmann::json j_number_float (nlohmann::json::value_t::number_float);
        j_number_float = Value;
        mpValue->push_back (j_number_float);
    }

    /***********************************************************************************/

    void SGParameter::Append (const int Value)
    {
        EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
        nlohmann::json j_number_integer (nlohmann::json::value_t::number_integer);
        j_number_integer = Value;
        mpValue->push_back (j_number_integer);
    }

    /***********************************************************************************/

    void SGParameter::Append (const bool Value)
    {
        EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
        nlohmann::json j_boolean (nlohmann::json::value_t::boolean);
        j_boolean = Value;
        mpValue->push_back (j_boolean);
    }

    /***********************************************************************************/

    void SGParameter::Append (const std::string& rValue)
    {
        EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
        nlohmann::json j_string (nlohmann::json::value_t::string);
        j_string = rValue;
        mpValue->push_back (j_string);
    }

    /***********************************************************************************/

    // void SGParameter::Append (const Vector& rValue)
    // {
    //     EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
    //     mpValue->push_back (nlohmann::json (rValue));
    // }

    /***********************************************************************************/

    // void SGParameter::Append (const Matrix& rValue)
    // {
    //     EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
    //     const SizeType nrows = rValue.size1 ();
    //     const SizeType ncols = rValue.size2 ();

    //     nlohmann::json j_col_array (0.0, ncols);
    //     nlohmann::json j_row_array (0.0, nrows);

    //     for (IndexType i = 0; i < nrows; ++i)
    //     {
    //         for (IndexType j = 0; j < ncols; ++j)
    //         {
    //             j_col_array[j] = rValue (i, j);
    //         }

    //         j_row_array[i] = j_col_array;
    //     }

    //     mpValue->push_back (j_row_array);
    // }

    /***********************************************************************************/

    void SGParameter::Append (const SGParameter& rValue)
    {
        EXCEPTION_IF_NOT (mpValue->is_array ()) << "It must be an Array parameter to append" << std::endl;
        nlohmann::json j_object = nlohmann::json (nlohmann::json::parse (rValue.WriteJsonString (), nullptr, true, true));
        mpValue->push_back (j_object);
    }

    /***********************************************************************************/

    void SGParameter::CopyValuesFromExistingParameters (const SGParameter               OriginParameters,
                                                        const std::vector<std::string>& rListParametersToCopy)
    {
        for (const auto& r_value_name : rListParametersToCopy)
        {
            if (OriginParameters.Has (r_value_name))
            {
                if (this->Has (r_value_name))
                {
                    SGError << r_value_name
                            << " already defined in destination (check keyword is not duplicated in the list) SGParameter:\n\n"
                            << this->PrettyPrintJsonString () << std::endl;
                }
                else
                {
                    this->AddValue (r_value_name, OriginParameters[r_value_name]);
                }
            }
            else
            {
                SGError << r_value_name << " not defined in origin SGParameter:\n\n"
                        << OriginParameters.PrettyPrintJsonString () << std::endl;
            }
        }
    }

    /***********************************************************************************/

    void SGParameter::RecursivelyFindValue (const nlohmann::json& rBaseValue, const nlohmann::json& rValueToFind) const
    {
        for (auto itr = rBaseValue.begin (); itr != rBaseValue.end (); ++itr)
        {
            const auto value = itr.value ();
            if (&(value) == &rValueToFind)
            {
                const std::string value_string = value.dump ();
                SGInfo << "Base = " << PrettyPrintJsonString () << "\nProblematic var name " << itr.key () << " value " << value_string
                       << std::endl;
            }
            else
            {
                if (itr->is_object ())
                    RecursivelyFindValue (value, rValueToFind);
                // TODO: it could be an array
            }
        }
    }

    /***********************************************************************************/

    bool SGParameter::IsEquivalentTo (SGParameter& rParameters)
    {
        for (auto itr = this->mpValue->begin (); itr != this->mpValue->end (); ++itr)
        {
            const std::string& r_item_name = itr.key ();

            bool found = false;

            for (auto& r_parameter_reference : rParameters.items ())
            {
                if (r_item_name == r_parameter_reference.key ())
                {
                    found                           = true;
                    SGParameter subobject           = (*this)[r_item_name];
                    SGParameter reference_subobject = rParameters[r_item_name];

                    if (itr->is_object ())
                    {
                        if (!subobject.IsEquivalentTo (reference_subobject))
                            return false;
                    }
                    else
                    {
                        if (itr.value () != r_parameter_reference.value ())
                            return false;
                    }
                    break;
                }
            }

            if (!found)
                return false;
        }

        // Reverse check: the rParameters can contain fields that are missing in the object
        for (auto& r_parameter : rParameters.items ())
        {
            const std::string& r_item_name = r_parameter.key ();

            bool found = false;

            for (auto& r_parameter_reference : this->items ())
            {
                if (r_item_name == r_parameter_reference.key ())
                {
                    found = true;
                    // No need to check the values here, if they were found in the previous loop, values were checked there
                    break;
                }
            }

            if (!found)
                return false;
        }

        return true;
    }

    /***********************************************************************************/

    bool SGParameter::HasSameKeysAndTypeOfValuesAs (SGParameter& rParameters)
    {
        for (auto itr = this->mpValue->begin (); itr != this->mpValue->end (); ++itr)
        {
            const std::string& r_item_name = itr.key ();

            bool found = false;

            for (auto& r_parameter_reference : rParameters.items ())
            {
                if (r_item_name == r_parameter_reference.key ())
                {
                    found                           = true;
                    SGParameter subobject           = (*this)[r_item_name];
                    SGParameter reference_subobject = rParameters[r_item_name];

                    if (itr->is_object ())
                    {
                        if (!subobject.HasSameKeysAndTypeOfValuesAs (reference_subobject))
                            return false;
                    }
                    else
                    {
                        if (itr.value ().type () != r_parameter_reference.value ().type ())
                        {
                            return false;
                        }
                    }
                    break;
                }
            }

            if (!found)
                return false;
        }

        // Reverse check: the rParameters can contain fields that are missing in the object
        for (auto& r_parameter : rParameters.items ())
        {
            const std::string& r_item_name = r_parameter.key ();

            bool found = false;

            for (auto& r_parameter_reference : this->items ())
            {
                if (r_item_name == r_parameter_reference.key ())
                {
                    found = true;
                    // No need to check the types here, if they were found in the previous loop, types were checked there
                    break;
                }
            }

            if (!found)
                return false;
        }

        return true;
    }

    /***********************************************************************************/

    void SGParameter::ValidateAndAssignDefaults (const SGParameter& rDefaultParameters)
    {
        try
        {
            this->ValidateDefaults (rDefaultParameters);
            this->AddMissingParameters (rDefaultParameters);
        }
        catch (const std::exception& e)
        {
            SGError << e.what () << '\n';
        }
    }

    /***********************************************************************************/

    void SGParameter::ValidateDefaults (const SGParameter& rDefaultParameters) const
    {
        try
        {
            // Verifies that all the entries in the current parameters have a correspondance in the rDefaultParameters.
            // If it is not the case throw an error
            for (auto itr = this->mpValue->begin (); itr != this->mpValue->end (); ++itr)
            {
                const std::string& r_item_name = itr.key ();
                if (!rDefaultParameters.Has (r_item_name))
                {
                    std::stringstream msg;
                    msg << "The item with name \"" << r_item_name << "\" is present in this SGParameter but NOT in the default values"
                        << std::endl;
                    msg << "Hence Validation fails" << std::endl;
                    msg << "SGParameter being validated are : " << std::endl;
                    msg << this->PrettyPrintJsonString () << std::endl;
                    msg << "Defaults against which the current parameters are validated are :" << std::endl;
                    msg << rDefaultParameters.PrettyPrintJsonString () << std::endl;
                    SGError << msg.str () << std::endl;
                }

                bool type_coincides = false;
                auto value_defaults = (rDefaultParameters[r_item_name]).GetUnderlyingStorage ();
                if (itr->is_number () && value_defaults->is_number ())
                    type_coincides = true;
                //             if(itr->is_number_integer() && value_defaults->is_number_integer()) type_coincides = true;
                //             if(itr->is_number_float() && value_defaults->is_number_float()) type_coincides = true;
                if (itr->is_boolean () && value_defaults->is_boolean ())
                    type_coincides = true;
                if (itr->is_null () && value_defaults->is_null ())
                    type_coincides = true;
                if (itr->is_array () && value_defaults->is_array ())
                    type_coincides = true;
                if (itr->is_string () && value_defaults->is_string ())
                    type_coincides = true;
                if (itr->is_object () && value_defaults->is_object ())
                    type_coincides = true;

                if (type_coincides == false)
                {
                    std::stringstream msg;
                    msg << "******************************************************************************************************"
                        << std::endl;
                    msg << "The item with name :\"" << r_item_name
                        << "\" does not have the same type as the corresponding one in the default values" << std::endl;
                    msg << "******************************************************************************************************"
                        << std::endl;
                    msg << "SGParameter being validated are : " << std::endl;
                    msg << this->PrettyPrintJsonString () << std::endl;
                    msg << "Defaults against which the current parameters are validated are :" << std::endl;
                    msg << rDefaultParameters.PrettyPrintJsonString () << std::endl;
                    SGError << msg.str () << std::endl;
                }
            }
        }
        catch (const std::exception& e)
        {
            SGError << e.what () << '\n';
        }
    }

    /***********************************************************************************/

    void SGParameter::AddMissingParameters (const SGParameter& rDefaultParameters)
    {
        try
        {
            // Iterate over all the rDefaultParameters. In the case a default value is not assigned in the current SGParameter add an item
            // copying its value
            if (rDefaultParameters.IsSubParameter ())
            {
                for (auto& r_parameter : rDefaultParameters.items ())
                {
                    const std::string& r_item_name = r_parameter.key ();
                    if (mpValue->find (r_item_name) == mpValue->end ())
                    {
                        (*mpValue)[r_item_name] = r_parameter.value ();
                    }
                }
            }
            /* code */
        }
        catch (const std::exception& e)
        {
            SGError << e.what () << '\n';
        }
    }

    /***********************************************************************************/

    void SGParameter::RecursivelyValidateAndAssignDefaults (const SGParameter& rDefaultParameters)
    {
        try
        {
            this->RecursivelyValidateDefaults (rDefaultParameters);
            this->RecursivelyAddMissingParameters (rDefaultParameters);
            /* code */
        }
        catch (const std::exception& e)
        {
            SGError << e.what () << '\n';
        }
    }

    /***********************************************************************************/

    void SGParameter::RecursivelyValidateDefaults (const SGParameter& rDefaultParameters) const
    {
        try
        {
            // Verifies that all the entries in the current parameters have a correspondance in the rDefaultParameters.
            // If it is not the case throw an error
            for (auto itr = this->mpValue->cbegin (); itr != this->mpValue->cend (); ++itr)
            {
                const std::string& r_item_name = itr.key ();

                if (!rDefaultParameters.Has (r_item_name))
                {
                    std::stringstream msg;
                    msg << "The item with name \"" << r_item_name << "\" is present in this SGParameter but NOT in the default values"
                        << std::endl;
                    msg << "Hence Validation fails" << std::endl;
                    msg << "SGParameter being validated are : " << std::endl;
                    msg << this->PrettyPrintJsonString () << std::endl;
                    msg << "Defaults against which the current parameters are validated are :" << std::endl;
                    msg << rDefaultParameters.PrettyPrintJsonString () << std::endl;
                    SGError << msg.str () << std::endl;
                }

                bool type_coincides = false;
                auto value_defaults = (rDefaultParameters[r_item_name]).GetUnderlyingStorage ();
                if (itr->is_number () && value_defaults->is_number ())
                    type_coincides = true;
                //             if(itr->is_number_integer() && value_defaults->is_number_integer()) type_coincides = true;
                //             if(itr->is_number_float() && value_defaults->is_number_float()) type_coincides = true;
                if (itr->is_boolean () && value_defaults->is_boolean ())
                    type_coincides = true;
                if (itr->is_null () && value_defaults->is_null ())
                    type_coincides = true;
                if (itr->is_array () && value_defaults->is_array ())
                    type_coincides = true;
                if (itr->is_string () && value_defaults->is_string ())
                    type_coincides = true;
                if (itr->is_object () && value_defaults->is_object ())
                    type_coincides = true;

                if (type_coincides == false)
                {
                    std::stringstream msg;
                    msg << "The item with name :\"" << r_item_name
                        << "\" does not have the same type as the corresponding one in the default values" << std::endl;
                    msg << "SGParameter being validated are : " << std::endl;
                    msg << this->PrettyPrintJsonString () << std::endl;
                    msg << "Defaults against which the current parameters are validated are :" << std::endl;
                    msg << rDefaultParameters.PrettyPrintJsonString () << std::endl;
                    SGError << msg.str () << std::endl;
                }

                // Now walk the tree recursively
                if (itr->is_object ())
                {
                    SGParameter subobject          = (*this)[r_item_name];
                    SGParameter defaults_subobject = rDefaultParameters[r_item_name];
                    subobject.RecursivelyValidateDefaults (defaults_subobject);
                }
            }
            /* code */
        }
        catch (const std::exception& e)
        {
            SGError << e.what () << '\n';
        }
    }

    /***********************************************************************************/

    void SGParameter::RecursivelyAddMissingParameters (const SGParameter& rDefaultParameters)
    {
        try
        {
            // Now iterate over all the rDefaultParameters. In the case a default value is not assigned in the current SGParameter add an
            // item copying its value
            if (rDefaultParameters.IsSubParameter ())
            {
                for (auto itr = rDefaultParameters.mpValue->begin (); itr != rDefaultParameters.mpValue->end (); ++itr)
                {
                    const std::string& r_item_name = itr.key ();

                    if (mpValue->find (r_item_name) == mpValue->end ())
                    {
                        (*mpValue)[r_item_name] = itr.value ();
                    }

                    // Now walk the tree recursively
                    if (itr->is_object ())
                    {
                        SGParameter subobject          = (*this)[r_item_name];
                        SGParameter defaults_subobject = rDefaultParameters[r_item_name];

                        subobject.RecursivelyAddMissingParameters (defaults_subobject);
                    }
                }
            }
            /* code */
        }
        catch (const std::exception& e)
        {
            SGError << e.what () << '\n';
        }
    }

    /***********************************************************************************/

    SGParameter::SGParameter (nlohmann::json* pValue, std::shared_ptr<nlohmann::json> pRoot) : mpValue (pValue), mpRoot (pRoot)
    {}

    /***********************************************************************************/

    SGParameter::SGParameter (json_iterator itValue, nlohmann::json* pValue, std::shared_ptr<nlohmann::json> pRoot)
        : mpValue (nullptr), mpRoot (pRoot)
    {
        if (itValue != pValue->end ())
            mpValue = &(*itValue);
    }

    /***********************************************************************************/

    SGParameter::SGParameter (json_const_iterator itValue, nlohmann::json* pValue, std::shared_ptr<nlohmann::json> pRoot)
        : mpValue (nullptr), mpRoot (pRoot)
    {
        if (itValue != pValue->cend ())
            mpValue = const_cast<nlohmann::json*> (&(*itValue));
    }

    /***********************************************************************************/

    nlohmann::json* SGParameter::GetUnderlyingStorage ()
    {
        return mpValue;
    }

    /***********************************************************************************/

    nlohmann::json* SGParameter::GetUnderlyingStorage () const
    {
        return mpValue;
    }

    /***********************************************************************************/

    void SGParameter::SetUnderlyingSotrage (nlohmann::json* pNewValue)
    {
        mpValue = pNewValue;
    }

    /***********************************************************************************/

    std::shared_ptr<nlohmann::json> SGParameter::GetUnderlyingRootStorage ()
    {
        return mpRoot;
    }

    /***********************************************************************************/

    std::shared_ptr<nlohmann::json> SGParameter::GetUnderlyingRootStorage () const
    {
        return mpRoot;
    }

    /***********************************************************************************/

    void SGParameter::SetUnderlyingRootStorage (std::shared_ptr<nlohmann::json> pNewValue)
    {
        mpRoot = pNewValue;
    }

    /***********************************************************************************/

    void SGParameter::InternalSetValue (const SGParameter& rOtherValue)
    {
        delete[] mpValue;
        mpValue = new nlohmann::json (nlohmann::json::parse (rOtherValue.WriteJsonString (), nullptr, true, true));
    }

    // 判断是否为复数字符串
    bool SGParameter::IsComplex () const
    {
        if (!mpValue->is_string ())
            return false;
        std::string s = mpValue->get<std::string> ();
        std::regex  re (R"(^([+-]?\d+(\.\d+)?)([+-]\d+(\.\d+)?)?i$)");
        return std::regex_match (s, re);
    }

    std::complex<double> SGParameter::GetComplex () const
    {
        EXCEPTION_IF_NOT (IsComplex ()) << "Argument must be a complex string like 1.0i or 2.5+3.1i" << std::endl;
        std::string s = mpValue->get<std::string> ();
        // 简单解析，仅支持形如 "1.0i" 或 "2.5+3.1i"
        double real = 0.0, imag = 0.0;
        size_t pos = s.find ('i');
        if (pos != std::string::npos)
        {
            std::string num = s.substr (0, pos);
            if (num.empty () || num == "+")
                imag = 1.0;
            else if (num == "-")
                imag = -1.0;
            else
                imag = std::stod (num);
        }
        // 支持 a+bi 形式
        pos = s.find ('+');
        if (pos != std::string::npos && s.back () == 'i')
        {
            real = std::stod (s.substr (0, pos));
            imag = std::stod (s.substr (pos, s.size () - pos - 1));
        }
        else if (s.find ('-') != std::string::npos && s.back () == 'i' && s[0] != '-')
        {
            pos = s.find ('-', 1);
            if (pos != std::string::npos)
            {
                real = std::stod (s.substr (0, pos));
                imag = std::stod (s.substr (pos, s.size () - pos - 1));
            }
        }
        return std::complex<double> (real, imag);
    }

    void SGParameter::SetComplex (const std::complex<double>& val)
    {
        std::ostringstream oss;
        oss << val.real ();
        if (val.imag () >= 0)
            oss << "+";
        oss << val.imag () << "i";
        *mpValue = oss.str ();
    }

};  // namespace SG::Utility